package top.java.collectionTest;

import org.junit.Test;

import java.util.HashSet;
import java.util.Objects;
import java.util.Set;


/**
 *          集合、数组都是对多个数据进行存储操作的结构，简称Java容器。
 *
 *          Collection接口 ：单列数据      用来存储一个一个的对象
 *
 *              -Set接口：存储无序的，不可重复的数据           -->  高中讲的 "集合"
 *                  -HashSet
 *                      -LinkedHashSet
 *                  -TreeSet
 *
 *        说明：Set接口中没有额外定义新的方法，使用的都是Collection中声明过的方法
 *
 *        HashSet：作为Set接口的主要实现类，线程不安全的，可以存储null
 *        LinkedHashSet：作为HashSet的子类，遍历其内部数据时可以按照添加的顺序遍历
 *                      对于频繁的遍历操作，LinkedHashSet()效率高于HashSet()
 *        TreeSet：可以按照添加对象的指定属性，进行排序
 *
 *       Set：存储无序的，不可重复的数据  (以HashSet为例说明)
 *
 *       无序性：不等于随机性.存储的数据在底层数组中并非按照数组索引的顺序添加
 *
 *       不可重复性：保证添加的元素按照equals()判断时，不能返回true    即：相同元素只能添加一个
 *
 *       向Set(主要指：HashSet、LinkedHashSet)中添加的数据，其所在的类一定要重写hashCode()和equals()
 *       重写的hashCode()和equals()尽可能保持一致性：相等的对象必须具有相等的散列码
 *       重写两个方法的小技巧：对象中用作 equals() 方法比较的 Field，都应该用来计算 hashCode 值。
 *
 *   元素的添加过程 (以HashSet为例)：
 *
 *      我们向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法，计算元素a的哈希值，
 *     此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置（即为：索引位置，判断
 *     数组此位置上是否已经元素：
 *       如果此位置上没其他元素，则元素a添加成功。 --->情况1
 *       如果此位置上其他元素b(或以链表形式存在的多个元素，则比较元素a与元素b的hash值：
 *          如果hash值不相同，则元素a添加成功。--->情况2
 *          如果hash值相同，进而需要调用元素a所在类的equals()方法：
 *              equals()返回true,元素a添加失败
 *              equals()返回false,则元素a添加成功。--->情况3
 *
 *      对于添加成功的情况2和情况3而言：元素a 与已经存在指定索引位置上数据以链表的方式存储。
 *      jdk 7 :元素a放到数组中，指向原来的元素。
 *      jdk 8 :原来的元素在数组中，指向元素a
 *      总结：七上八下
 *
 *      HashSet底层：数组+链表的结构。（前提：jdk7)
 *
 */
public class SetTest1 {

    @Test
    public void test1() {
        Set set = new HashSet();
        set.add(123);
        set.add(456);
        set.add("AAA");
        System.out.println(set);            //[AAA, 456, 123]
        set.add(123);
        System.out.println(set);            //[AAA, 456, 123]
        set.add(new Person("Tom",20));   //Person没有重写equals和和hashCode
        set.add(new Person("Tom",20));  //Person没有重写equals和和hashCode

        //[AAA, Person{name='Tom', age=20}, 456, Person1{name='Tom', age=20}, 123]
        System.out.println(set);
        //没有重写自定义类的equals()和hashCode()方法，自定义类可以重复添加,且删除不成功
        //重写以后，相同元素对象只能添加一次
        set.remove("AAA");
        set.remove(456);
        set.remove(123);
        set.add(new Person1("Jon",22));     //Person1重写了equals和和hashCode
        set.add(new Person1("Jon",22));     //Person1重写了equals和和hashCode
        //[Person{name='Tom', age=20}, Person{name='Tom', age=20}, Person1{name='Jon', age=22}]
        System.out.println(set);

        System.out.println("*****************");
/*
        Set set1 = new LinkedHashSet();
        set1.add(123);
        set1.add(456);
        set1.add("AAA");
        set1.add(new Person("Tom",22));
        System.out.println(set1);           //[123, 456, AAA, Person{name='Tom', age=22}]
*/

        //没有重写hashCode()方法,相同对象属性的对象得出的哈希值不同
        System.out.println(new Person("Tom",22).hashCode());   //1313953385
        System.out.println(new Person("Tom",22).hashCode());   //399573350

    }

}

class Person1 {
    private String name;
    private int age;

    public Person1() {
    }

    public Person1(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person1{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person1 person1 = (Person1) o;
        return age == person1.age && Objects.equals(name, person1.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}
